#include <jni.h>
#include <string>
#include <android/log.h>
#include <speex/speex_bits.h>
#include <speex/speex.h>
#include <speex/speex_preprocess.h>
#include <speex/speex_echo.h>


#define LOG(...) __android_log_print(ANDROID_LOG_ERROR,"native_log",__VA_ARGS__)

SpeexBits eBits,dBits;
void * enc_state;
void * dec_state;
SpeexEchoState * echo_state;
SpeexPreprocessState * preprocess_state;

int enc_frame_size;
int dec_frame_size;
int code_open=0;
int sampleRate=8000;
int filter_length=160*8;


JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_echoCancel(JNIEnv *env, jobject instance, jshortArray lin_,
                                    jshortArray echo_, jshortArray out_) {
    jshort *lin = env->GetShortArrayElements(lin_, NULL);
    jshort *echo = env->GetShortArrayElements(echo_, NULL);
    jshort *out = env->GetShortArrayElements(out_, NULL);
    speex_echo_cancellation(echo_state,lin,echo,out);
    speex_preprocess_run(preprocess_state,out);
    env->ReleaseShortArrayElements(lin_, lin, 0);
    env->ReleaseShortArrayElements(echo_, echo, 0);
    env->ReleaseShortArrayElements(out_, out, 0);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_open(JNIEnv *env, jobject instance, jint quality) {

    if(code_open++!=0){
        return -1;
    }
    speex_bits_init(&eBits);
    speex_bits_init(&dBits);

    enc_state = speex_encoder_init(&speex_nb_mode);//初始化状态
    dec_state = speex_decoder_init(&speex_nb_mode);

    speex_encoder_ctl(enc_state,SPEEX_SET_QUALITY,&quality);//设置参数
    speex_encoder_ctl(enc_state,SPEEX_GET_FRAME_SIZE,&enc_frame_size);//获取参数
    speex_decoder_ctl(dec_state,SPEEX_GET_FRAME_SIZE,&dec_frame_size);
    int i=1;
    speex_decoder_ctl(dec_state,SPEEX_SET_ENH,&i);
    LOG("enc_state=%d",enc_frame_size);
    LOG("dec_state=%d",dec_frame_size);
    echo_state = speex_echo_state_init(enc_frame_size,filter_length);
    preprocess_state = speex_preprocess_state_init(enc_frame_size,sampleRate);
    speex_preprocess_ctl(preprocess_state,SPEEX_PREPROCESS_SET_ECHO_STATE,echo_state);

    return 0;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_getFrameSize(JNIEnv *env, jobject instance) {
    if(!code_open){
        return -1;
    }
    return dec_frame_size;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_encode(JNIEnv *env, jobject instance, jshortArray lin_, jint offset,
                                jbyteArray encoded_, jint size) {
    if(!code_open){
        return -1;
    }
    jshort lin[enc_frame_size];
    jbyte encoded[enc_frame_size];
    speex_bits_reset(&eBits);
    int count = (size-1)/enc_frame_size+1;//当数据超过编码帧的长度，分多次编码
    for(int i=0;i<count;i++){
        env->GetShortArrayRegion(lin_,offset+enc_frame_size*i,enc_frame_size,lin);
        speex_encode_int(enc_state,lin,&eBits);
    }
    int encode_size=speex_bits_write(&eBits, (char *) encoded, enc_frame_size);
    env->SetByteArrayRegion(encoded_,0,encode_size,encoded);
    return encode_size;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_decode(JNIEnv *env, jobject instance, jbyteArray encoded_,
                                jshortArray lin_, jint size) {
    if(!code_open){
        return -1;
    }
    jbyte encoded[dec_frame_size];
    jshort lin[dec_frame_size];
    env->GetByteArrayRegion(encoded_,0,size,encoded);
    speex_bits_read_from(&dBits, (char *) encoded, size);
    int result=speex_decode_int(dec_state,&dBits,lin);
    LOG("decode result=%d",result);
    env->SetShortArrayRegion(lin_,0,dec_frame_size,lin);
    return dec_frame_size;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_playback(JNIEnv *env, jobject instance, jshortArray echoFrame_) {
    short echoFrame[enc_frame_size];
    env->GetShortArrayRegion(echoFrame_,0,enc_frame_size,echoFrame);
    speex_echo_playback(echo_state,echoFrame);
    return 0;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_capture(JNIEnv *env, jobject instance, jshortArray inputFrame_,
                                 jshortArray outFrame_) {
    jshort inputFrame[enc_frame_size];
    jshort outFrame[enc_frame_size];
    env->GetShortArrayRegion(inputFrame_,0,enc_frame_size,inputFrame);
    speex_echo_capture(echo_state,inputFrame,outFrame);
    speex_preprocess_run(preprocess_state,outFrame);
    env->SetShortArrayRegion(outFrame_,0,enc_frame_size,outFrame);
    return 0;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_hyh_speex_Speex_close(JNIEnv *env, jobject instance) {
    if(--code_open!=0){
        return -1;
    }
    speex_encoder_destroy(enc_state);
    speex_bits_destroy(&eBits);
    speex_decoder_destroy(dec_state);
    speex_bits_destroy(&dBits);
    speex_preprocess_state_destroy(preprocess_state);
    speex_echo_state_destroy(echo_state);
    return 0;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_hyh_videochat_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
